// Copyright 2017 Proyectos y Sistemas de Mantenimiento SL (eProsima).
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// ------------------------------------------------------------------
// Modification history:
// feature: Unified Transport Abstraction Layer Unified Transport Abstraction Layer
// feature: Support load dynamic_types with same name from xml
// feature: QoS Profile Inheritance and Composition
// feature: simplify xml profile
// ------------------------------------------------------------------

#include <ertps/xmlparser/XMLProfileManager.h>
#include <tinyxml2.h>
#include <xmlparser/XMLTreePart.h>
#include <xmlparser/XMLParserPart.h>
#include <xmlparser/XMLProfileManagerPart.h>

#include <transport/TransportDescriptorInterface.h>

#include <xmlparser/XMLProfileManagerPart.h>
#include <xmlparser/attributes/XMLParticipantAttributes.h>
#include <ertps/xmlparser/XMLProfileManager.h>

#ifdef _WIN32
#include <windows.h>
#endif  // ifdef _WIN32

#include <cstdlib>
#include <elog/Log.hpp>

namespace evbs {
namespace ertps {
namespace xmlparser {

using evbs::ertps::LibrarySettingsAttributes;
using xmlfile_map_iterator_t = xmlfiles_map_t::iterator;
LibrarySettingsAttributes XMLProfileManager::library_settings_;
sp_transport_map_t XMLProfileManager::transport_profiles_;
std::mutex XMLProfileManager::mtx_xml_transport_profiles_;
p_dynamictype_map_t XMLProfileManager::dynamic_types_;
std::mutex XMLProfileManager::mtx_xml_dynamic_types_;
std::vector<std::string> XMLProfileManager::userTransportsId;
std::map<std::string, XMLP_ret> XMLProfileManager::xml_files_;
std::recursive_mutex XMLProfileManager::mtx_xml_files_;

XMLP_ret XMLProfileManager::checkTransportId() {
    // participant中的transportid都应该在模板中找到
    if (vbsutil::xmlparser::XMLProfileManagerPart::userTransportsId.empty()) {
        return XMLP_ret::XML_OK;
    }
    for (const auto& str1 : vbsutil::xmlparser::XMLProfileManagerPart::userTransportsId) {
        for (const auto& str2 : XMLProfileManager::userTransportsId) {
            if (str1 == str2) {
                return XMLP_ret::XML_OK;  // 匹配成功
            }
        }
    }
    elogError(XMLPARSER, RetCode_t::RETCODE_ERROR,
              "not found transportid ,please cheack participant <userTransports/transport_id> "
              "<transport_descriptors/transport_id>");
    for (const auto& str1 : vbsutil::xmlparser::XMLProfileManagerPart::userTransportsId) {
        elogError(XMLPARSER, RetCode_t::RETCODE_ERROR, "participant <userTransports/transport_id>'" << str1 << "'");
    }
    for (const auto& str2 : XMLProfileManager::userTransportsId) {
        elogError(XMLPARSER, RetCode_t::RETCODE_ERROR, "<transport_descriptors/transport_id>'" << str2 << "'");
    }
    return XMLP_ret::XML_ERROR;
}

void XMLProfileManager::loadDefaultXMLFile() {
    // Try to load the default XML file set with an environment variable.
    /*c++API no getenv_s, use getenv*/
#if 0
    char file_path[MAX_PATH];
    size_t size = MAX_PATH;
    if (getenv_s(&size, file_path, size, DEFAULT_ERTPS_ENV_VARIABLE) == 0 && size > 0) {
        loadXMLFile(file_path);
    }

    // Should take into account '\0'
    char skip_xml[2];
    size = 2;

    // Try to load the default XML file if variable does not exist or is not set to '1'
    if (!(getenv_s(&size, skip_xml, size, SKIP_DEFAULT_XML_FILE) == 0 && skip_xml[0] == '1')) {
        loadXMLFile(DEFAULT_ERTPS_PROFILES);
    }
#else

    if (const char* file_path = std::getenv(xmlString::DEFAULT_ERTPS_ENV_VARIABLE)) {
        (void)loadXMLFile(file_path);
        logDebug(XMLPARSER, "load xml file from env. file path:" << file_path);
    }

    const char* skip_xml = std::getenv(xmlString::SKIP_DEFAULT_XML_FILE);

    // Try to load the default XML file if variable does not exist or is not set to '1'
    if (!((skip_xml != nullptr) && (skip_xml[0] == '1'))) {
        (void)loadXMLFile(xmlString::DEFAULT_ERTPS_PROFILES);
        logDebug(XMLPARSER, "load default config:" << xmlString::DEFAULT_ERTPS_PROFILES);
    }

#endif  // ifdef _WIN32
}

XMLP_ret XMLProfileManager::loadXMLProfiles(tinyxml2::XMLElement& profiles) {
    vbsutil::xmlparser::up_base_node_t root_node;
    if (strcmp(profiles.Name(), xmlString::PROFILES) != 0) {
        elogError(XMLPARSER, RetCode_t::RETCODE_ERROR, "<profiles> element not found");
        return XMLP_ret::XML_ERROR;
    }

    if (XMLParser::loadXMLProfiles(profiles, root_node) == XMLP_ret::XML_OK) {
        logDebug(XMLPARSER, "Node parsed successfully");
        return XMLP_ret::XML_OK;
    } else {
        elogError(XMLPARSER, RetCode_t::RETCODE_ERROR, "Error parsing profiles");
        return XMLP_ret::XML_ERROR;
    }
}

XMLP_ret XMLProfileManager::loadXMLDynamicTypes(tinyxml2::XMLElement& types) {
    return XMLParser::loadXMLDynamicTypes(types);
}

XMLP_ret XMLProfileManager::loadXMLNode(tinyxml2::XMLDocument& doc) {
    // 先调用xml独立库解析，再解析evbs中需要解析的模块
    XMLP_ret loaded_ret = vbsutil::xmlparser::XMLProfileManagerPart::loadXMLNode(doc);
    if (loaded_ret != XMLP_ret::XML_OK) {
        elogError(XMLPARSER, RetCode_t::RETCODE_ERROR, "Error parsing xmlpart node");
        return XMLP_ret::XML_ERROR;
    }

    loaded_ret = XMLParser::loadXML(doc, vbsutil::xmlparser::XMLProfileManagerPart::xmlNode_root_node);
    if (loaded_ret != XMLP_ret::XML_OK) {
        elogError(XMLPARSER, RetCode_t::RETCODE_ERROR, "Error parsing node");
        return XMLP_ret::XML_ERROR;
    }

    logDebug(XMLPARSER, "Node parsed successfully");
    return XMLP_ret::XML_OK;
}

XMLP_ret XMLProfileManager::loadXMLFile(const std::string& filename, bool no_repeat) {
    // 先调用xml独立库解析，再解析evbs中需要解析的模块
    std::lock_guard<std::recursive_mutex> lk(mtx_xml_files_);
    XMLP_ret loaded_ret = vbsutil::xmlparser::XMLProfileManagerPart::loadXMLFile(filename);
    if (loaded_ret != XMLP_ret::XML_OK) {
        if (filename != std::string(xmlString::DEFAULT_ERTPS_PROFILES)) {
            elogError(XMLPARSER, RetCode_t::RETCODE_ERROR, "Error parsing xmlpart'" << filename << "'");
        }
        return XMLP_ret::XML_ERROR;
    }

    if (no_repeat) {
        xmlfile_map_iterator_t it = xml_files_.find(filename);
        if ((it != xml_files_.end()) && (XMLP_ret::XML_OK == it->second)) {
            logDebug(XMLPARSER, "XML file '" << filename << "' already parsed");
            return XMLP_ret::XML_OK;
        }
    }

    loaded_ret = XMLParser::loadXML(filename, vbsutil::xmlparser::XMLProfileManagerPart::xmlFile_root_node);
    (void)xml_files_.emplace(filename, loaded_ret);

    if (loaded_ret != XMLP_ret::XML_OK) {
        if (filename != std::string(xmlString::DEFAULT_ERTPS_PROFILES)) {
            elogError(XMLPARSER, RetCode_t::RETCODE_ERROR, "Error parsing'" << filename << "'");
        }
        return XMLP_ret::XML_ERROR;
    }
    if (XMLProfileManager::checkTransportId() != XMLP_ret::XML_OK) {
        return XMLP_ret::XML_ERROR;
    }

    logInfo(XMLPARSER, "File '" << filename << "' parsed successfully");
    return XMLP_ret::XML_OK;
}

XMLP_ret XMLProfileManager::loadXMLString(const char* data, size_t length) {
    // 先调用xml库解析，再解析evbs中字段
    XMLP_ret loaded_ret = vbsutil::xmlparser::XMLProfileManagerPart::loadXMLString(data, length);
    if (loaded_ret != XMLP_ret::XML_OK) {
        elogError(XMLPARSER, RetCode_t::RETCODE_ERROR, "Error parsing  xmlpart xmlstring");
        return XMLP_ret::XML_ERROR;
    }

    loaded_ret = XMLParser::loadXML(data, length, vbsutil::xmlparser::XMLProfileManagerPart::xmlString_root_node);
    if (loaded_ret != XMLP_ret::XML_OK) {
        elogError(XMLPARSER, RetCode_t::RETCODE_ERROR, "Error parsing string");
        return XMLP_ret::XML_ERROR;
    }
    return XMLP_ret::XML_OK;
}

bool XMLProfileManager::insertTransportById(const std::string& transport_id, sp_transport_t transport) {
    std::lock_guard<std::mutex> lk(mtx_xml_transport_profiles_);
    if (transport_profiles_.find(transport_id) == transport_profiles_.end()) {
        transport_profiles_[transport_id] = std::move(transport);
        return true;
    }
    elogWarning(XMLPARSER,
                "Error adding the transport " << transport_id << ". There is other transport with the same id");
    return false;
}

const LibrarySettingsAttributes& XMLProfileManager::library_settings() {
    return library_settings_;
}

void XMLProfileManager::library_settings(const LibrarySettingsAttributes& library_settings) {
    library_settings_ = library_settings;
}

sp_transport_t XMLProfileManager::getTransportById(const std::string& transport_id) {
    std::lock_guard<std::mutex> lk(mtx_xml_transport_profiles_);
    if (transport_profiles_.find(transport_id) != transport_profiles_.end()) {
        return transport_profiles_[transport_id];
    }
    return nullptr;
}

bool XMLProfileManager::insertDynamicTypeByName(const std::string& type_name, p_dynamictypebuilder_t type) {
    std::lock_guard<std::mutex> lk(mtx_xml_dynamic_types_);
    //support loading types with same name more than once.
    dynamic_types_[type_name] = type;
    return true;
}

p_dynamictypebuilder_t XMLProfileManager::getDynamicTypeByName(const std::string& type_name) {
    std::lock_guard<std::mutex> lk(mtx_xml_dynamic_types_);
    if (dynamic_types_.find(type_name) != dynamic_types_.end()) {
        return dynamic_types_[type_name];
    }
    return nullptr;
}
#if 0
types::DynamicPubSubType* XMLProfileManager::CreateDynamicPubSubType(const std::string& type_name) {
    std::lock_guard<std::mutex> lk(mtx_xml_dynamic_types_);
    if (dynamic_types_.find(type_name) != dynamic_types_.end()) {
        return new types::DynamicPubSubType(dynamic_types_[type_name]->build());
    }
    return nullptr;
}
#endif

}  // namespace xmlparser
}  // namespace ertps
}  // namespace evbs
